package com.ibeeking.gateway.cs.config.filter;

import com.ibeeking.gateway.cs.constant.LogUtils;
import com.ibeeking.gateway.cs.constant.ThreadContextCache;
import com.ibeeking.found.common.constants.HeaderNounConstant;
import com.ibeeking.found.common.enums.LoginRoleEnum;
import com.ibeeking.found.system.api.common.param.RefreshTokenParam;
import com.ibeeking.found.system.api.feign.UaaCSClient;
import com.ibeeking.nematos.constants.enums.LogConstant;
import com.ibeeking.nematos.utils.data.StringUtils;
import com.ibeeking.nematos.utils.result.ResponseResult;
import com.alibaba.fastjson.JSONObject;
import com.nimbusds.jose.JWSObject;
import lombok.SneakyThrows;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.List;

/**
 * 黑名单token过滤器
 *
 * @Author ibeeking
 */
@Component
@RefreshScope
public class GlobalFilter implements org.springframework.cloud.gateway.filter.GlobalFilter, Ordered {
    private static final Logger log = LoggerFactory.getLogger(GlobalFilter.class);

    @Resource
    private UaaCSClient uaaCSClient;
    @Value("${ibeeking.tokenExpires:1800}")
    private Long tokenExpires;

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();

        //解析token
        JSONObject tokenObject = analysisToken(request.getHeaders().getFirst(HeaderNounConstant.AUTHORIZATION));
        ServerHttpRequest newRequest = addHeader(request, tokenObject);
        try {
            String newToken = refreshToken(tokenObject);
            if (HttpStatus.UNAUTHORIZED.getReasonPhrase().equals(newToken)) {
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            } else if (StringUtils.isNotBlank(newToken)) {
                ServerHttpResponse response = exchange.getResponse();
                response.getHeaders().add(HeaderNounConstant.AUTHORIZATION, newToken);
                return chain.filter(exchange.mutate().request(newRequest).response(response).build());
            }
            return chain.filter(exchange.mutate().request(newRequest).build());
        } finally {
            ThreadContext.clearAll();
        }
    }

    private String refreshToken(JSONObject tokenObject) throws ParseException {
        if (null == tokenObject) {
            return "";
        }
        //判断时间是否达到超时时间
        Long expTime = Long.valueOf(tokenObject.get("exp").toString());
        if (expTime * 1000 - System.currentTimeMillis() <= tokenExpires * 1000) {
            //刷新token
            String tokenId = tokenObject.get(HeaderNounConstant.JWT_JTI).toString().replaceAll("-", "");
            RefreshTokenParam refreshTokenParam = new RefreshTokenParam();
            refreshTokenParam.setClientId(tokenObject.get(HeaderNounConstant.CLIENT_ID).toString());
            refreshTokenParam.setJti(tokenId);
            ResponseResult<String> responseResult = uaaCSClient.refreshToken(refreshTokenParam);
            if (StringUtils.isNotBlank(responseResult.getData())) {
                return responseResult.getData();
            }
        }
        return "";
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    /**
     * 添加请求头信息
     *
     * @param request
     * @return
     * @throws ParseException
     */
    private ServerHttpRequest addHeader(ServerHttpRequest request, JSONObject tokenObject) throws ParseException {
        ServerHttpRequest build = request.mutate().build();
        HttpHeaders headers = new HttpHeaders();
        headers.putAll(request.getHeaders());
        headers.add(LogConstant.TRACE_ID, request.getId());
        String industry;
        String userId;
        String userName;
        String amDemoUser;
        String tenantId;
        String tokenId;
        String loginSystem;
        String loginRole;
        if (null != tokenObject) {
            industry = tokenObject.get(HeaderNounConstant.INDUSTRY).toString();
            userId = tokenObject.get(HeaderNounConstant.USER_ID).toString();
            userName = tokenObject.get(HeaderNounConstant.USER_NAME).toString();
            amDemoUser = tokenObject.get(HeaderNounConstant.AM_DEMO_USER).toString();
            tenantId = tokenObject.get(HeaderNounConstant.TENANT_ID).toString();
            tokenId = tokenObject.get(HeaderNounConstant.JWT_JTI).toString().replaceAll("-", "");
            List<String> roles = (List<String>) tokenObject.get(HeaderNounConstant.AUTHORITY_CLAIM_NAME);
            loginSystem = roles.get(0);
            if (roles.size() > 1) {
                loginRole = roles.get(1);
            } else {
                loginRole = roles.get(0);
            }
        } else {
            industry ="";
            userId = "";
            userName = "";
            amDemoUser = "false";
            tenantId = "";
            tokenId = "";
            loginSystem = LoginRoleEnum.CUSTOMER_SERVICE.name();
            loginRole = LoginRoleEnum.CUSTOMER_SERVICE.name();
        }
        headers.add(HeaderNounConstant.LOGIN_SYSTEM, loginSystem);
        headers.add(HeaderNounConstant.LOGIN_ROLE, loginRole);
        headers.add(HeaderNounConstant.TENANT_ID, tenantId);
        headers.add(HeaderNounConstant.USER_ID, userId);
        headers.add(HeaderNounConstant.INDUSTRY, industry);
        headers.add(HeaderNounConstant.AM_DEMO_USER, amDemoUser);
        try {
            headers.add(HeaderNounConstant.USER_NAME, URLEncoder.encode(userName, StandardCharsets.UTF_8.name()));
        } catch (UnsupportedEncodingException e) {
            LogUtils.error("中文编码异常[{}]", userName);
        }
        headers.add(HeaderNounConstant.JWT_JTI, tokenId);

        //往本地线程添加信息
        ThreadContextCache.threadContext(build.getId(),
                build.getHeaders().getFirst(HeaderNounConstant.TENANT_ID),
                build.getHeaders().getFirst(HeaderNounConstant.USER_NAME),
                build.getHeaders().getFirst(HeaderNounConstant.USER_ID),
                build.getHeaders().getFirst(HeaderNounConstant.AM_DEMO_USER),
                industry
        );
        return new ServerHttpRequestDecorator(build) {
            @Override
            public HttpHeaders getHeaders() {
                return headers;
            }
        };
    }

    /**
     * 解析token
     *
     * @param token token
     * @return
     * @throws ParseException
     */
    private JSONObject analysisToken(String token) throws ParseException {
        JSONObject jsonObject = null;
        if (StringUtils.isNotBlank(token)) {
            String payload = JWSObject.parse(token.replace(HeaderNounConstant.JWT_PREFIX, Strings.EMPTY)).getPayload().toString();
            jsonObject = JSONObject.parseObject(payload);
        }
        return jsonObject;
    }
}
